import sys
import time
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
    QApplication,
    QWidget,
    QGridLayout,
    QLabel,
    QScrollArea,
    QVBoxLayout,
    QPushButton,
    QStackedWidget,
    QGraphicsDropShadowEffect
)
from PyQt5.QtCore import Qt, QTimer
from PyQt5.QtSerialPort import QSerialPort, QSerialPortInfo
from serial_thread import SerialThread
from circular_progress_bar import CircularProgressBar
from device_detail_widget import DeviceDetailWidget


class CardListApp(QWidget):
    """设备测试管理主界面"""

    # 在 CardListApp 类的 __init__ 方法中添加一个字典来保持对进度条的强引用
    def __init__(self):
        super().__init__()
        # 初始化UI布局
        self.initUI()

        # 设备数据存储
        self.device_detail_widgets = []  # 设备详情页面实例
        self.serial_ports = []  # 有效串口列表
        self.serial_instances = []  # 已打开的串口实例
        self.threads = []  # 测试线程列表
        self.completed_threads = 0  # 已完成测试线程计数器
        # 增加无效端口缓存
        self.invalid_ports_cache = {}  # 格式: {port_name: invalid_time}
        self.cache_expire = 300  # 缓存有效期5分钟（单位：秒）
        self.progress_bars = {}  # 存储进度条 {port_name: progress_bar}
        self.task_labels = {}  # 存储状态标签
        self._init_serial_detector()

    def initUI(self):
        """初始化主界面布局"""
        # 窗口尺寸设置
        screen_geometry = QApplication.primaryScreen().geometry()
        self.resize(screen_geometry.width() // 2, screen_geometry.height() // 2)

        # 主布局设置
        main_layout = QVBoxLayout(self)
        self.stacked_widget = QStackedWidget()

        # 设备列表界面
        self.device_list_widget = QWidget()
        self.stacked_widget.addWidget(self.device_list_widget)

        main_layout.addWidget(self.stacked_widget)
        self.setWindowTitle("设备测试管理系统")

    def _init_serial_detector(self):
        from serial_detector import SerialPortDetector
        from PyQt5.QtCore import QThread

        # 创建检测线程
        self.detector_thread = QThread()
        self.detector = SerialPortDetector()

        # 修正信号连接方式（添加连接类型参数）
        self.detector.portListUpdated.connect(
            self._handle_port_update, Qt.QueuedConnection
        )

        # 将对象移到线程前完成信号连接
        self.detector.moveToThread(self.detector_thread)

        # 修改线程启动逻辑
        self.detector_thread.start()

    def _handle_port_update(self, new_ports):
        """处理端口更新信号"""
        # 检查是否有实际变化，避免不必要的UI刷新
        if set(self.serial_ports) == set(new_ports):
            return
            
        # 记录变化情况
        added_ports = [p for p in new_ports if p not in self.serial_ports]
        removed_ports = [p for p in self.serial_ports if p not in new_ports]
        
        if added_ports:
            print(f"新增串口: {added_ports}")
        if removed_ports:
            print(f"移除串口: {removed_ports}")
        
        self.serial_ports = new_ports
        
        # 使用延迟刷新，避免频繁更新UI
        if not hasattr(self, '_refresh_timer'):
            self._refresh_timer = QTimer()
            self._refresh_timer.setSingleShot(True)
            self._refresh_timer.timeout.connect(self.refresh_ui_layout)
        
        # 如果定时器已经在运行，重置它
        if self._refresh_timer.isActive():
            self._refresh_timer.stop()
        
        # 设置200ms后刷新UI
        self._refresh_timer.start(200)

    def refresh_ui_layout(self):
        """刷新设备列表界面"""
        # 保存当前端口列表，用于清理无效引用
        current_ports = self.serial_ports.copy()
        
        # 清理无效的进度条引用
        invalid_ports = [port for port in list(self.progress_bars.keys()) if port not in current_ports]
        for port in invalid_ports:
            print(f"清理无效进度条引用: {port}")
            self.progress_bars.pop(port, None)
        
        # 清理无效的任务标签引用
        invalid_labels = [port for port in list(self.task_labels.keys()) if port not in current_ports]
        for port in invalid_labels:
            print(f"清理无效状态标签引用: {port}")
            self.task_labels.pop(port, None)
        
        # 清理旧布局
        if self.device_list_widget.layout():
            QWidget().setLayout(self.device_list_widget.layout())
        
        # 创建新布局
        self.create_device_list(self.serial_ports)

    def create_device_list(self, ports):
        """创建设备卡片列表
        Args:
            ports (list): 有效串口列表
        """
        # 布局初始化
        layout = QVBoxLayout(self.device_list_widget)

        # 开始测试按钮
        self.init_start_button(layout)

        # 滚动区域设置
        scroll_area = QScrollArea()
        scroll_area.setWidgetResizable(True)  # 允许内容区域调整大小
        container = QWidget()
        
        # 使用网格布局，并设置合适的间距
        grid_layout = QGridLayout(container)
        grid_layout.setSpacing(10)  # 减小卡片之间的垂直/水平间距
        grid_layout.setContentsMargins(5, 5, 5, 5)  # 减少容器边距
        
        # 设置布局对齐策略为左上对齐
        grid_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop)
        
        # 重置所有列的伸缩因子为0（不自动拉伸）
        for col in range(10):
            grid_layout.setColumnStretch(col, 0)
        
        # 计算每行应该显示的卡片数量（根据窗口宽度动态计算）
        # 考虑到卡片最大宽度为350，加上间距
        cards_per_row = max(1, min(4, (self.width() - 10) // 350))  # 更精确的宽度计算

        # 卡片生成
        for idx, port in enumerate(ports):
            card = self.create_device_card(port, idx)
            row, col = divmod(idx, cards_per_row)  # 动态计算行列
            # 移除居中对齐，使用默认对齐方式
            grid_layout.addWidget(card, row, col)  # 不再使用Qt.AlignCenter

            # 添加设备详情页
            if idx >= len(self.device_detail_widgets):
                self.device_detail_widgets.append(
                    DeviceDetailWidget(port, self.show_device_list)
                )

        scroll_area.setWidget(container)
        layout.addWidget(scroll_area)
        
        # 存储当前的卡片布局信息，用于窗口大小变化时重新布局
        self.current_container = container
        self.current_ports = ports
        self.current_cards_per_row = cards_per_row

    def create_device_card(self, port_name, idx):
        """创建单个设备卡片
        Args:
            port_name (str): 串口名称
            idx (int): 设备索引
        Returns:
            QWidget: 卡片实例
        """
        # 卡片容器
        card = QWidget()
        # 设置最大尺寸限制，防止卡片过大
        card.setMaximumSize(350, 300)  # 限制最大宽度和高度
        
        # 保持一定的最小尺寸，确保内容正常显示
        card.setMinimumSize(250, 200)
        
        card_layout = QVBoxLayout(card)
        card_layout.setSpacing(10)  # 设置内部组件间距

        # 卡片组件
        title = QLabel(port_name)
        title.setAlignment(Qt.AlignCenter)
        title.setStyleSheet("font-size: 16px; font-weight: bold;")
        
        progress_bar = CircularProgressBar()
        progress_bar.setFixedSize(100, 100)  # 固定进度条大小
        
        task_label = QLabel("等待测试...")
        task_label.setAlignment(Qt.AlignCenter)
        
        # 存储界面组件
        self.progress_bars[port_name] = progress_bar
        self.task_labels[port_name] = task_label

                # 样式设置
        card.setStyleSheet(
            """
        QWidget { 
            background:#fff; 
            border:1px solid #e0e0e0;
            border-radius:8px; 
            padding:10px; 
            margin:2px;
        }
        QWidget:hover { border-color:#4CAF50; }
        """
        )
        # 添加阴影效果
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(15)
        shadow.setXOffset(2)
        shadow.setYOffset(2)
        shadow.setColor(QColor(0, 0, 0, 60))
        card.setGraphicsEffect(shadow)

        # 事件绑定
        card.mousePressEvent = lambda event, i=idx: self.open_device_detail(i)

        # 组件布局
        card_layout.addStretch()
        card_layout.addWidget(title)
        card_layout.addWidget(progress_bar, alignment=Qt.AlignCenter)
        card_layout.addWidget(task_label)
        card_layout.addStretch()

        return card
    
    def init_start_button(self, layout):
        """Initialize and configure the start test button"""
        self.start_button = QPushButton("开始测试", self.device_list_widget)
        self.start_button.setStyleSheet(
            """
            QPushButton {
                background-color: #4CAF50;
                color: white;
                font-size: 16px;
                font-weight: bold;
                padding: 10px 20px;
                border: none;
                border-radius: 5px;
            }
            QPushButton:hover { background-color: #45a049; }
        """
        )
        self.start_button.clicked.connect(self.on_start_button_clicked)
        layout.addWidget(self.start_button, alignment=Qt.AlignCenter)

    def resizeEvent(self, event):
        """处理窗口大小变化事件"""
        super().resizeEvent(event)
        
        # 如果当前显示的是设备列表，且有设备，则重新计算布局
        if (hasattr(self, 'current_ports') and 
            self.current_ports and 
            self.stacked_widget.currentWidget() == self.device_list_widget):
            
            # 计算新的每行卡片数量
            new_cards_per_row = max(1, self.width() // 300)
            
            # 如果每行卡片数量变化，重新布局
            if new_cards_per_row != self.current_cards_per_row:
                # 使用延迟刷新，避免频繁更新UI
                if not hasattr(self, '_resize_timer'):
                    self._resize_timer = QTimer()
                    self._resize_timer.setSingleShot(True)
                    self._resize_timer.timeout.connect(self.refresh_ui_layout)
                
                # 如果定时器已经在运行，重置它
                if self._resize_timer.isActive():
                    self._resize_timer.stop()
                
                # 设置100ms后刷新UI
                self._resize_timer.start(100)

    def open_device_detail(self, idx):
        """打开设备测试详情"""

        detail_widget = self.device_detail_widgets[idx]
        # 更新堆叠窗口，显示设备详情
        self.stacked_widget.addWidget(detail_widget)
        self.stacked_widget.setCurrentWidget(detail_widget)

    def show_device_list(self):
        """返回设备列表界面"""
        self.stacked_widget.setCurrentWidget(self.device_list_widget)
    
    def _update_test_progress(self, port_name, step_index, status):
        """根据测试步骤更新进度"""
        try:
            total_steps = 3  # 假设总共有3个测试步骤
            progress = int((step_index + 1) / total_steps * 100)

            # 添加调试信息
            print(f"更新进度条: {port_name}, 步骤: {step_index}, 状态: {status}, 进度: {progress}%")

            # 更新进度条 - 添加安全检查
            if port_name in self.progress_bars:
                progress_bar = self.progress_bars[port_name]
                if progress_bar and not progress_bar.isHidden() and progress_bar.isVisible():
                    try:
                        progress_bar.setValue(progress)
                    except RuntimeError as e:
                        print(f"进度条已被删除，移除引用: {port_name}, 错误: {e}")
                        # 移除无效引用
                        self.progress_bars.pop(port_name, None)
            else:
                print(f"错误: 找不到端口 {port_name} 的进度条")

            # 更新步骤状态显示 - 添加安全检查
            for detail_widget in self.device_detail_widgets:
                if detail_widget.device_name == port_name:
                    try:
                        detail_widget.updateSteps(port_name, step_index, status)
                    except Exception as e:
                        print(f"更新步骤状态失败: {e}")

            # 更新状态文本 - 添加安全检查
            if port_name in self.task_labels:
                task_label = self.task_labels[port_name]
                if task_label and not task_label.isHidden() and task_label.isVisible():
                    status_text = {
                        "in_progress": ["数据上报中...", "参数配置中...", "最终验证中..."],
                        "finished": ["上报成功", "配置完成", "验证通过"],
                        "failed": ["上报失败", "配置失败", "验证失败"],
                    }
                    if status in status_text and step_index < len(status_text[status]):
                        try:
                            task_label.setText(status_text[status][step_index])
                        except RuntimeError:
                            print(f"状态标签已被删除，移除引用: {port_name}")
                            self.task_labels.pop(port_name, None)
            else:
                print(f"错误: 找不到端口 {port_name} 的状态标签")
        except Exception as e:
            print(f"更新进度条异常: {e}")

    def _update_test_detail(self, port_name, step_index, detail_text):
        """更新测试详情信息
        
        Args:
            port_name (str): 串口名称
            step_index (int): 测试步骤索引
            detail_text (str): 详情文本
        """
        # 查找对应的设备详情页
        for detail_widget in self.device_detail_widgets:
            # 修改这里，使用 device_name 而不是 port_name
            if detail_widget.device_name == port_name:
                # 更新对应步骤的详情文本
                detail_widget.update_step_detail(step_index, detail_text)
                break

    def on_start_button_clicked(self):
        self.start_button.setEnabled(False)  # 禁用按钮，防止重复点击
        self.start_button.setText("测试中...")  # 改变按钮文本
        
        # 暂停串口检测，避免测试过程中设备列表被更新
        if hasattr(self, 'detector') and self.detector:
            self.detector.pause_detection()  # 添加暂停检测方法
        
        # 清理之前的线程
        for thread in self.threads:
            if thread.isRunning():
                thread.stop()
                thread.wait()  # 等待线程结束
        
        self.threads.clear()
        self.completed_threads = 0
        
        # 为每个串口创建测试线程
        for port in self.serial_ports:
            thread = SerialThread(port)
            # 连接信号
            thread.updateStepSignal.connect(self._update_test_progress)
            thread.updateDetailSignal.connect(self._update_test_detail)
            thread.finished.connect(self._on_thread_finished)
            
            # 保存线程引用
            self.threads.append(thread)
            
            # 重置进度条
            if port in self.progress_bars:
                self.progress_bars[port].setValue(0)
            if port in self.task_labels:
                self.task_labels[port].setText("等待测试...")
        
        # 启动所有线程
        for thread in self.threads:
            thread.start()

    def _on_thread_finished(self):
        """当线程完成时调用此方法"""
        try:
            # 添加线程安全校验
            if not hasattr(self, 'completed_threads'):
                self.completed_threads = 0
                
            self.completed_threads += 1
            
            # 检查是否所有线程都已完成
            if self.completed_threads >= len(self.threads):
                self.start_button.setEnabled(True)
                self.start_button.setText("开始测试")
                
                # 恢复串口检测
                if hasattr(self, 'detector') and self.detector:
                    self.detector.resume_detection()  # 添加恢复检测方法
        except Exception as e:
            print(f"线程完成处理异常: {str(e)}")
